package com.javasm.mingming.common.config;

import com.alibaba.fastjson.JSON;
import com.javasm.mingming.common.utils.TokenUtil;
import com.javasm.mingming.login.json.JsonLoginFilter;
import com.javasm.mingming.login.jwt.JwtFilter;
import com.javasm.mingming.login.jwt.JwtProvider;
import com.javasm.mingming.login.phone.PhoneFilter;
import com.javasm.mingming.login.phone.PhoneProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author javasm
 * @version 0.1
 * @className SecurityConfig
 * @descriptioin:
 * @date 2025/1/11 14:47
 * @since jdk11
 */
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //跨域
    @Bean
    public CorsFilter corsFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        //配置放过的域名
        //SpringBoot 2.3.12版本中,可以直接写*
        //更高版本,需要精确的写明 放过哪个域名 http://127.0.0.1:5173  http://192.168.0.42:5173
        config.addAllowedOrigin("*");
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");
        config.addAllowedHeader(TokenUtil.Server_Token_Key);
        config.setAllowCredentials(true);//放过cookie
        source.registerCorsConfiguration("/**",config);
        return new CorsFilter(source);
    }

    //加密方式添加到容器中,会自动识别自动生效
    @Bean
    public PasswordEncoder passwordEncoder(){
        //指定 当前密码的加密算法
        return new BCryptPasswordEncoder();
    }

    /********************JSON登录开始************************/
    @Resource(name = "usernameUserDetailsService")
    UserDetailsService usernameUserDetailsService;
    @Bean
    public JsonLoginFilter jsonLoginFilter() throws Exception {
        JsonLoginFilter jsonLoginFilter = new JsonLoginFilter();
        //必须有
        jsonLoginFilter.setAuthenticationManager(authenticationManagerBean());
        //配置 成功之后显示什么
        jsonLoginFilter.setAuthenticationSuccessHandler((request, response, authentication) -> createJSON(response,authentication));
        //失败
        jsonLoginFilter.setAuthenticationFailureHandler((request, response, e) -> createJSON(response,e.getMessage()));
        //访问的路径
        jsonLoginFilter.setFilterProcessesUrl("/jsonlogin");
        return jsonLoginFilter;
    }
    @Bean
    public DaoAuthenticationProvider jsonProvider(){
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        //配置  哪个service 去查询用户信息
        authenticationProvider.setUserDetailsService(usernameUserDetailsService);
        //密码加密的算法
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }
    /********************JSON登录结束************************/
    /********************手机号登录开始************************/
    @Resource
    PhoneProvider phoneProvider;
    @Bean
    public PhoneFilter phoneFilter() throws Exception {
        PhoneFilter phoneFilter = new PhoneFilter();
        phoneFilter.setAuthenticationManager(authenticationManagerBean());
        //配置 成功之后显示什么
        phoneFilter.setAuthenticationSuccessHandler((request, response, authentication) -> createJSON(response,authentication));
        //失败
        phoneFilter.setAuthenticationFailureHandler((request, response, e) -> createJSON(response,e.getMessage()));
        return phoneFilter;
    }
    /********************手机号登录结束************************/

    /********************Token登录开始************************/
    @Resource
    JwtProvider jwtProvider;
    @Bean
    public JwtFilter jwtFilter() throws Exception {
        JwtFilter jwtFilter = new JwtFilter();
        jwtFilter.setAuthenticationManager(authenticationManagerBean());
        //配置 成功之后显示什么
        jwtFilter.setAuthenticationSuccessHandler((request, response, authentication) -> createJSON(response,authentication));
        //失败
        jwtFilter.setAuthenticationFailureHandler((request, response, e) -> createJSON(response,e.getMessage()));
        return jwtFilter;
    }
    /********************Token登录结束************************/

    @Bean
    public RoleHierarchy roleHierarchy(){
        RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
        //super_admin权限,大于 admin
        hierarchy.setHierarchy("ROLE_super_admin > ROLE_admin");
        return hierarchy;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //http对象 ,支持链式调用
        //.antMatchers(匹配这个路径).permitAll(),符合规则的,都放过
        //所有的请求,都需要登录(认证)之后才能访问
        http.authorizeRequests()
                //地址/test1/f1只有角色super_admin可以访问,其他没有提到的地址,是没有设置权限的,只要认证成功,都可以访问
                ///test1/**
                //.antMatchers("/test1/f1").hasRole("super_admin")
                //.antMatchers("/test1/f2").hasRole("admin")
                //.antMatchers("/test1/f3","/test1/f4").hasAnyRole("super_admin","admin")
                //↑角色授权 ↓权限标识授权
                //.antMatchers("/test1/f5").hasAuthority("admin")
                //.antMatchers("/test1/f6").hasAuthority("test")
                //.antMatchers("/test1/f7").hasAnyAuthority("admin","test")

                .antMatchers("/loginuser/**","/web/**").permitAll()
                .anyRequest().authenticated();
        //必须放到其他过滤器之前
        http.addFilterBefore(corsFilter(), UsernamePasswordAuthenticationFilter.class);
        //把json登录的过滤器,添加到过滤器链中
        http.addFilterAt(jsonLoginFilter(), UsernamePasswordAuthenticationFilter.class)
                .authenticationProvider(jsonProvider());
        //phone登录过滤器配置到过滤器链中
        http.addFilterAt(phoneFilter(),UsernamePasswordAuthenticationFilter.class)
                        .authenticationProvider(phoneProvider);
        //Token登录过滤器
        http.addFilterAt(jwtFilter(),UsernamePasswordAuthenticationFilter.class)
                        .authenticationProvider(jwtProvider);
        //表单登录
        http.formLogin()
                .loginPage("/login.html")//跳转的登录页面
                .usernameParameter("uname")//配置表单中的用户名 name属性
                .passwordParameter("pwd")//表单中的密码 name属性
                .loginProcessingUrl("/javasmlogin")//表单提交的路径,action的路径
                //.defaultSuccessUrl("/login_success.html",true)
                .successHandler((request, response, authentication) -> createJSON(response,authentication))
                .failureHandler((request, response, e) -> createJSONError(response,e))
                .permitAll();//以上提到的路径,都不需要登录就能访问
        //退出登录
        http.logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler((request,response,obj)-> createJSON(response,obj))
                .permitAll();
        //未登录
        http.exceptionHandling()
                .authenticationEntryPoint((request, response, e) -> createJSON(response,"未登录"));
        //定义403
        http.exceptionHandling().accessDeniedHandler((request,response,e)->createJSON(response,"无权限"));

        //关闭csrf攻击  一定记得关
        http.csrf().disable();
    }

    private void createJSONError(HttpServletResponse response,Exception e){
        response.setContentType("application/json;charset=utf-8");
        Map<String,Object> map = new ConcurrentHashMap<>();
        map.put("code",500);
        map.put("msg",e.getMessage());
        PrintWriter printWriter = null;
        try {
            printWriter = response.getWriter();
        } catch (IOException e1) {
            throw new RuntimeException(e1);
        }
        printWriter.write(JSON.toJSONString(map));
        printWriter.flush();
        printWriter.close();
    }
    private void createJSON(HttpServletResponse response,Object obj){
        response.setContentType("application/json;charset=utf-8");
        Map<String,Object> map = new ConcurrentHashMap<>();
        map.put("code",200);
        map.put("msg","success");
        if (obj != null)
            map.put("data",obj);
        PrintWriter printWriter = null;
        try {
            printWriter = response.getWriter();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        printWriter.write(JSON.toJSONString(map));
        printWriter.flush();
        printWriter.close();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //配置 放过  静态资源的
        //绕开了Security过滤器链
        web.ignoring().antMatchers("/css/**","/js/**","/**/*.jpg","/webUser/**");
    }
}
